function cb(callback, p0,p1,p2) {
    try{
        if( callback && callback && typeof callback){
            callback(p0,p1,p2);
        }
    }
    catch (e) {
        console.log(e);
    }
};
function extend( a , b) {
    try{
        if( a==null || !a){
            a = {};
        }
        if( b && b!=null){
            for( p in b ){
                a[p] = b[p];
            }
        }
    }
    catch (e) {
        console.log(e);
    }
};
function TKmsClient(){
    this.config={
        appName: "hhRemoteAssist",
        apiUrl: "https://wss.hhdata.cn:20022/devapi/",
        kms:{
            kmsTagKey:"kmsTag_v10",
            kurentUrl: "wss://wss.hhdata.cn:8433/kurento",
            kmsPeerOptions: {
                configuration: {
                    "iceServers": [
                        {
                            urls: "stun:223.68.161.139:3478"
                        },
                        {
                            urls: ["turn:223.68.161.139:3478"],
                            username: "kurento",
                            credential: "kurento"
                        }
                    ]
                },
                mediaConstraints: {
                    audio: true,
                    video: {width: {exact: 1280}, height: {exact: 720}}
                }
            },
            kmsPeerTypes: {
                send: kurentoUtils.WebRtcPeer.WebRtcPeerSendonly,
                rec: kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly,
                sendRec: kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv
            } ,
            localVideo:null ,
            remoteVideo:null ,
            hubFlag:false
        },
        mq:{
            host:"gmggf8g.mqtt.iot.bj.baidubce.com",
            port:443,
            uid:"gmggf8g/peer",
            pwd:"a52bwKg22dCAuQzB",
            channelTopic:"hhkms/channel",
            sessionTopic:"hhkms/session",
            sessionId: ""
        },
        busi:{
            appName: "hhRemoteAssist",
            apiUrl: "https://wss.hhdata.cn:20022/devapi/",
            busiId:"",
            channelId:"",
            busiInfo:{},
            localHostId:"",
            localKey:"busi.v1.localKey"
        }.localHostId,
        infos:{
            channelId:"",
            channelInfo:{},
            sessionInfo:{},
            players:[]
        },
    };
    this.datas ={
        devices:{},
        kms:{
            kurentoClient: null ,
            pipe: null,
            hub: null ,
            peer: null,
            end: null ,
            endHubport: null ,
            players:[]
        },
        mqClient: null ,
        sessions:{},
        registData:{}
    };
    this.events={
        onSessionChanged: function (msg) {} ,
        onMsg: function (topic, msg) {}
    };
    this.callApi = function (ac, ps, callback) {
        var me = this;
        try {
            var url = me.config.busi.apiUrl + ac + "?tmrnd=" + (new Date()).getTime().toString();
            /*
            $.post( url , ps , function (res) {
                    try{
                        var result = JSON.parse(res);
                        hhls.callBack(callback , result);
                    }
                    catch (er0) {
                        console.log(er0);
                    }
                } );
             */
            $.ajax({
                url: url,
                data: ps,
                //type:"POST",
                //contentType:"JSON",
                success: function (res) {
                    try {
                        var result = JSON.parse(res);
                        cb(callback, result);
                    } catch (er0) {
                        console.log(er0);
                    }
                },
                error: function (errRes) {
                    try {
                        cb(callback, errRes);
                    } catch (er0) {

                    }
                }
            })
        } catch (e) {
            console.log(e)
        }
    };
    this.dbQuery = function (key, ps, callback) {
        var me = this;
        try {
            var ac = "dbQuery";
            var psJson = JSON.stringify(ps);
            var psData = {
                appName: me.config.busi.appName,
                key: key,
                ps: psJson
            };
            me.callApi(ac, psData, callback);
        } catch (e) {
            console.log(e)
        }
    };
    this.getDevices = function (callback) {
        var me = this;
        try{

        }
        catch (e) {
            console.log(e);
        }
    };
    this.getSessionId = function () {
        var me = this;
        try{
            if( (!me.config.infos.sessionId) ||
                me.config.infos.sessionId ==null ||
                me.config.infos.sessionId==""){
                me.config.infos.sessionId = ['kmsid' , ( new Date()).getTime() , parseInt(Math.random()*100000)].join('_');
            }
        }
        catch (e) {
            console.log(e);
        }
        return me.config.infos.sessionId;
    };
    this.getHostId = function () {
        var me = this;
        var res ="";
        try{
            var aKey="hostId";
            var hid = window.localStorage.getItem(aKey);
            if( (!hid) || hid==null || hid.length==0 ){
                hid = ['hostId' , ( new Date()).getTime() , parseInt(Math.random()*100000)].join('_');
            }
            res = hid;
        }
        catch (e) {
            console.log(e);
        }
        return res;
    };
    this.getKmsObjData = function (kmsObj , callback) {
        var me = this;
        try{
            kmsObj.getTag(me.config.kms.kmsTagKey, function (tagJson) {
                if(tagJson && tagJson!=null){
                    try{
                        kmsObj[me.config.kms.kmsTagKey] = JSON.parse(tagJson);
                        kmsObj.getName(function (erName , name) {
                            kmsObj.name = name;
                            cb(callback , kmsObj);
                        });
                    }
                    catch (ex) {
                        cb(callback, kmsObj);
                    }
                }
            })
        }
        catch (e) {
            console.log(e);
        }
    };
    this.setKmsObjData = function (kmsObj ,key , info, callback) {
        var me = this;
        try{
            kmsObj.setName(key , function (erName) {
                var tagInfo = extend({},info);
                var infoJson = JSON.stringify(tagInfo);
                kmsObj.addTag(me.config.kms.kmsTagKey , infoJson, function (erTag) {
                    kmsObj.name = key;
                    kmsObj[me.config.kms.kmsTagKey] = tagInfo;
                    cb(callback, kmsObj);
                });
            })
        }
        catch (e) {
            console.log(e);
        }
    };
    this.getKmsClient = function (callback) {
        var me = this;
        try{
            if( me.datas.kms.kurentoClient  && me.datas.kms.kurentoClient!=null){
                cb(callback,me.datas.kms.kurentoClient);
            }
            else{
                kurentoClient(me.config.kms.kurentUrl, function (error, client) {
                    if (client) {
                        me.datas.kms.kurentoClient = client;
                    }
                    cb(callback,client);
                });
            }

        }
        catch (e) {
            console.log(e);
        }
    };
    this.loadChannel = function (key , info , callback) {
        var me = this;
        try{
            me.getKmsClient(function (client) {
                client.getServerManager(function (erSvr , svr) {
                   svr.getPipelines(function (erPs , ps ) {
                       var index =0;
                       var doOnGetPipe = function (pipe) {
                           if( me.config.kms.hubFlag>0){
                               pipe.getTag('hubKmsId', function (erTag , tag) {
                                   if( tag&& tag!=null && tag.length>0){
                                       client.getMediaobjectById(tag , function (erHub, hub) {
                                           pipe.hub = hub;
                                           me.datas.kms.pipe = pipe;
                                           cb(callback, pipe);
                                       })
                                   }
                                   else{
                                       pipe.create("Composite", function (erhub, hub) {
                                           pipe.addTag('hubKmsId', hub.id, function (erNewHhu) {
                                               pipe.hub = hub;
                                               me.datas.kms.pipe = pipe;
                                               cb(callback, pipe);
                                           })
                                       })
                                   }
                               })
                           }
                           else {
                               me.datas.kms.pipe = pipe;
                               cb(callback, pipe);
                           }
                       }
                       var checkPipe = function () {
                           if( index< ps.length){
                               var p = ps[index];
                               p.getName(function (erName , pipeName) {
                                   if( pipeName == key){
                                       me.getKmsObjData( p , function () {
                                           me.datas.kms.pipe = pipe;
                                           doOnGetPipe(p);
                                       })
                                   }
                                   else{
                                       index++;
                                       checkPipe();
                                   }
                               });
                           }
                           else{
                                var p = client.create("MediaPipeline" , function (erNewPipe ) {
                                    me.setKmsObjData(p , key , info, function () {
                                        doOnGetPipe(p);
                                    });
                                })
                           }
                       };
                       checkPipe();
                   }) 
                });
            })
        }
        catch (e) {
            console.log(e);
        }
    };
    this.loadChannelPlayers = function ( channelPipe , players, callback) {
        var me = this;
        try{
            var nodes=[];
            channelPipe.getChildren(function (erChild , childs) {
                var index =0;
                var children = childs.filter(function (c) {
                    return c.id.indexOf(".PlayerEndpoint")>0;
                });
                var getItem = function () {
                    if( index <children.length){
                        var kmsObj = children[index];
                        me.getKmsObjData(kmsObj , function () {
                            nodes.push(kmsObj);
                            var ps = players.filter(function (playerItem) {
                                return  playerItem.id == kmsObj.name;
                            });
                            if( ps.length>0){
                                ps[0].kmsId = kmsObj.id ;
                                ps[0].kmsObj = kmsObj;
                            }
                            index++;
                            getItem();
                        })
                    }
                    else{
                        var newPlayers = players.filter(function (item) {
                            return !(item.kmsId && item.kmsId.length> 0 && item.kmsObj);
                        });
                        index =0;
                        var newPlayer = function () {
                            if( index< newPlayers.length){
                                var npInfo = newPlayers[index];
                                channelPipe.create("PlayerEndpoint" , { uri: npInfo.url} , function (erNp, np) {
                                    np.on('EndOfStream', function () {
                                        np.play();
                                    });
                                    npInfo.kmsId = np.id;
                                    npInfo.kmsObj = np;
                                    np.play(function (erPlayer) {
                                        me.setKmsObjData(np , npInfo.id, npInfo , function () {
                                            if( channelPipe.hub){
                                                channelPipe.hub.createHubPort(function (erHubPort , newHubPort) {
                                                    np.hubPort = newHubPort;
                                                    np.connect(newHubPort);
                                                    index++;
                                                    newPlayer();
                                                })
                                            }
                                            else{
                                                index++;
                                                newPlayer();
                                            }
                                        });
                                    });
                                })
                            }
                            else{
                                cb(callback , players);
                            }
                        };
                        newPlayer();
                    }
                };
                getItem();
            });
        }
        catch (e) {
            console.log(e);
        }
    };
    this.createPeer = function (onCreatePeer) {
        var me = this;
        try{
            var opts = JSON.parse(JSON.stringify(me.config.kms.kmsPeerOptions));
            opts.localVideo = me.config.kms.localVideo;
            opts.remoteVideo = me.config.kms.remoteVideo;
            var peer = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(opts, function () {
                me.kms.datas.peer = peer;
                me.getDevices(function () {
                    cb(onCreatePeer, peer);
                })
            });
        }
        catch (e) {
            console.log(e);
        }
    };
    this.createEnd = function (channelPipe , callback) {
        var me = this;
        try{
            var sessionId = me.getSessionId();
            var hostId = me.getHostId();
            channelPipe.create("WebRtcEndpoint" , function (erEnd , end ) {
                me.datas.kms.end = end;
                var endInfo = extend(me.config.infos.sessionInfo, {
                    sessionId: sessionId,
                    hostId: hostId
                });
                if( channelPipe.hub){
                    channelPipe.hub.createHubPort(function (erNewPort , port) {
                        end.hubPort = port;
                        endInfo.hubPortKmsId = port.id;
                        me.setKmsObjData(end, endInfo.sessionId, endInfo, function () {
                            cb(callback, end)
                        });
                    })
                }
                else {
                    me.setKmsObjData(end, endInfo.sessionId, endInfo, function () {
                        cb(callback, end)
                    });
                }
            })
        }
        catch (e) {
            console.log(e);
        }
    };
    this.setHDWebRtcEndpoint = function ( end, onSet ) {
        var me = this;
        try{
            end.setMaxVideoRecvBandwidth(3000).then(()=>{
                return end.setMaxVideoRecvBandwidth(3000);
            }).then(()=>{
                return end.setMaxVideoSendBandwidth(3000);
            }).then(()=>{
                return end.setMinVideoRecvBandwidth(1500);
            }).then(()=>{
                return end.setMinVideoSendBandwidth(1500);
            }).then(()=>{
                cb( onSet)
            });
        }
        catch (e) {
            console.log(e);
        }
    };
    this.bindPeerEnd = function (peer , endpoint , callback) {
        var me = this;
        try{
            var setCandidateEvent = function (peer, endpoint) {
                try {
                    if (peer && endpoint) {
                        endpoint.on('OnIceCandidate', function (event) {
                            var candidate = event.candidate;
                            peer.addIceCandidate(candidate);
                            console.log(candidate);
                            console.log('candidate w->p')
                        });
                        peer.on('icecandidate', function (candidate) {
                            var iceCandidate = kurentoClient.register.complexTypes.IceCandidate(candidate);
                            endpoint.addIceCandidate(iceCandidate);
                            console.log(iceCandidate,candidate);
                            console.log('candidate p->w')

                        });
                    }
                } catch (e) {
                    ;
                }
            }
            if (endpoint && peer) {
                peer.generateOffer(function (errorPeerGenerateOffer, sdpOffer) {
                    if (errorPeerGenerateOffer && callback) {
                        callback(errorPeerGenerateOffer, null);
                    } else {
                        endpoint.processOffer(sdpOffer, function (errorSdpOffer, sdpAnswer) {
                            if (errorSdpOffer && callback) {
                                console.log("process sdpOffer error.", errorSdpOffer)
                                callback(errorSdpOffer, null);
                            } else {
                                setCandidateEvent(peer, endpoint);
                                peer.processAnswer(sdpAnswer);
                                endpoint.gatherCandidates(function (errorGatherCandidates) {
                                    if (errorGatherCandidates && callback) {
                                        console.log('webRtcPoint gatherCandidates error.', errorGatherCandidates)
                                        callback(errorGatherCandidates, null);
                                    } else {
                                        peer.endpoint = endpoint;
                                        endpoint.peer = peer;
                                        setCandidateEvent(peer, endpoint);
                                        if (callback) {
                                            callback(null, {endpoint: endpoint, peer: peer});
                                        }
                                    }
                                });
                            }
                        });
                    }
                });
            } else {
                if (callback) {
                    callback({error: "endPoint or peer is null."}, null);
                }
            }

        }
        catch (e) {
            console.log(e);
        }
    };

    this.acOpenChannel = function (callback) {
        var me = this;
        try{
            me.loadChannel( me.config.infos.channelInfo , me.config.infos.channelInfo,function (pipe) {
                me.loadChannelPlayers( me.datas.kms.pipe, me.config.infos.players, function () {
                    me.createPeer(function () {
                        me.createEnd(function (end) {
                            me.setHDWebRtcEndpoint(end , function () {
                                me.bindPeerEnd(me.datas.kms.peer , me.datas.kms.end , function () {
                                    cb(callback);
                                })
                            })
                        })
                    })
                })
            })
        }
        catch (e) {
            console.log(e);
        }
    };
    this.acRegist = function (callback) {
        var me = this;
        try{
            var client = null;
            var createConnection = function () {
                try{
                    var sessionId = me.getSessionId();
                    var hostId = me.getHostId();
                    var willTopic = [me.config.mq.channelTopic,me.config.infos.channelId].join("/");
                    var willMsg={
                        ac:"acOffline" ,
                        channelId: me.config.infos.channelId ,
                        sessionId: sessionId
                    };
                    var onConnect = function(){
                        try{
                            var subChannelTopic = [me.config.mq.channelTopic , me.config.infos.channelId , "#"].join("/");
                            var subSessionTopic = [me.config.mq.sessionId , me.config.infos.channelId, sessionId , "#"].join("/");
                            client.subscribe(subChannelTopic);
                            client.subscribe(subSessionTopic);
                            var helloMsg = {
                                ac:"acHello",
                                sessionId:sessionId , hostId: hostId , endKmsId: me.datas.kms.end.id ,
                                info:me.config.infos.sessionInfo
                            };
                            var helloMsgJson = JSON.stringify(helloMsg);
                            var publishChannelTopic = [me.config.mq.channelTopic , me.config.infos.channelId  ].join("/");
                            var handler = function () {
                                client.publish( publishChannelTopic, helloMsgJson , 0 , false );
                            };
                            window.setTimeout(handler , 2000);
                        }
                        catch (erc) {
                            console.log(erc);
                        }
                    };
                    var onDisconnect = function(){
                        try{
                            console.log('disconnect.');
                        }
                        catch (erc) {
                            console.log(erc);
                        }
                    };
                    var onMsg = function (result , data) {
                        var topic = result.Topic ;
                        var msg = JSON.parse(result.MsgContent);
                        if( msg.ac =="acOffline"){
                            var sid = msg.sessionId;
                            if( me.datas.sessions[sid]){
                                delete me.datas.sessions[sid];
                                cb( me.events.onSessionChanged , msg);
                            }
                        }
                        else if( msg.ac =="acHello"){
                            if( msg.sessionId!=sessionId){
                                me.datas.sessions[msg.sessionId] = msg;
                                cb( me.events.onSessionChanged, msg);
                            }
                            var welcomeMsg = JSON.parse(JSON.stringify(msg));
                            welcomeMsg.ac ="acWelcome";
                            var sessionTopic = [me.config.mq.sessionTopic,me.config.infos.channelId , me.config.infos.sessionId].join("/");
                            client.publish( sessionTopic, JSON.stringify(welcomeMsg) , 0 , false );
                        }
                        else if( msg.ac =="acWelcome"){
                            me.datas.sessions[msg.sessionId] = msg;
                            cb( me.events.onSessionChanged, msg);
                        }
                        else{
                            cb( me.events.onMsg , topic , msg);
                        }
                    };
                    client = createMqClient(
                        me.config.mq.host ,
                        me.config.mq.port ,
                        me.config.mq.uid ,
                        me.config.mq.pwd ,
                        me.config.mq.sessionId ,
                        "",
                        1 ,
                        willTopic,
                        willMsg,
                        onConnect ,
                        onDisconnect ,
                        onMsg
                    );
                    var getConnectionOptions = function () {
                        var res ={};
                        try{
                            res = {
                                invocationContext: {
                                    host: me.config.mq.host,
                                    port: me.config.mq.port,
                                    path: "",
                                    clientId: sessionId},
                                timeout: 10000,
                                keepAliveInterval: 60,
                                cleanSession: true,
                                useSSL: window.location.href.toLowerCase().indexOf("https://")>=0,
                                reconnect: true,
                                userName :me.config.mq.uid,
                                password :me.config.mq.pwd ,
                                onSuccess: function(e){
                                    //GateWay.Callback( GateWay.Events.OnConnected , GateWay);
                                },
                                onFailure:function(e){
                                    cb(onDisconnect , e);
                                }
                            };
                            var willMsgFlag =true;
                            if( willMsgFlag){
                                var wmsg = new Paho.Message(JSON.stringify(willMsg));
                                wmsg.destinationName = willTopic;
                                res.willMessage = wmsg;
                            }
                        }
                        catch (er) {
                            console.log(er);
                        }
                        return res;
                    };
                    client.connectOptions = getConnectionOptions();
                }
                catch (er) {
                    console.log(er);
                }
            }
            createConnection();
            var opts = client.connectOptions ;
            client.connect(opts);
            me.datas.mqClient = client;
        }
        catch (e) {
            console.log(e);
        }
    };
    this.acSetCamera = function ( peer ,  cameraDeviceId) {
        var me = this;
        try{
            if (window.rtcVideoStream) {
                window.rtcVideoStream.getTracks().forEach(track => {
                    if( track.kind=="video") {
                        track.stop();
                    }
                });
            }
            var constraints = {video: {deviceId: newCameraDeviceId , height:{exact:720} , width:{exact:1280}}};
            var sender= null;
            navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
                try {
                    window.rtcVideoStream = stream;
                    peer.localVideo.srcObject = stream;
                    let videoTrack = stream.getVideoTracks()[0];
                    sender = peer.peerConnection.getSenders().find(function (s) {
                        return s.track.kind == videoTrack.kind;
                    });
                    sender.replaceTrack(videoTrack);
                    cb(onSetCamera);
                }
                catch (eSet) {
                    cb( onSetCamera);
                }
            }).catch(function () {
                cb(onSetCamera);
            });
        }
        catch (e) {
            console.log(e);
        }
    };
    this.acSwitchCamera = function (peer) {
        var me = this;
        try{
            var ids =[
                me.datas.devices.videoinput[0].deviceId ,
                me.datas.devices.videoinput[me.datas.devices.videoinput.length-1].deviceId
            ];
            var cameraId = (
                peer.cameraId  &&
                peer.cameraId !=null &&
                peer.cameraId.length>0 )?peer.cameraId : "";
            cameraId = cameraId == ids[1]?ids[0]:ids[0];
            me.acSetCamera(newId , function () {
                peer.cameraId = cameraId;
            })
        }
        catch (e) {
            console.log(e);
        }
    };
    this.acToggleAudioMute = function (peer) {
        var me = this;
        try{
            peer.peerConnection.audioEnabled = !peer.peerConnection.audioEnabled;
        }
        catch (e) {
            console.log(e);
        }
    };
    this.acToggleVideoMute = function (peer) {
        var me = this;
        try{
            peer.peerConnection.videoEnabled = !peer.peerConnection.videoEnabled;
        }
        catch (e) {
            console.log(e);
        }
    };
    this.acTogglePlayVolume = function (peer) {
        var me = this;
        try{
            peer.remoteVideo.volume = peer.remoteVideo.volume==0?1:0;
        }
        catch (e) {
            console.log(e);
        }
    };
    this.acShareScreen = function (peer) {
        var me = this;
        try{
            var wWidth = $(window).width();
            var wHeight = $(window).height();
            var screenConstraints = {video: {width: {max:wWidth}, height: {max: wHeight}}, audio: true};
            var sender= null;
            if (navigator.mediaDevices.getDisplayMedia) {
                navigator.mediaDevices.getDisplayMedia(screenConstraints).then(
                    function (stream) {
                        let videoTrack = stream.getVideoTracks()[0];
                        peer.localVideo.srcObject = stream;
                        sender = peer.peerConnection.getSenders().find(function (s) {
                            return s.track.kind == videoTrack.kind;
                        });
                        sender.track.stop();
                        sender.replaceTrack(videoTrack);
                    }
                )
            }
        }
        catch (e) {
            console.log(e);
        }
    };
};
